﻿using Plossum.CommandLine;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClrExporter
{
    class Program
    {
        static int Main(string[] args)
        {
            Options options = new Options();
            CommandLineParser parser = new CommandLineParser(options);
            parser.Parse();

            Console.WriteLine(parser.UsageInfo.GetHeaderAsString(78));
            

            if (options.Help)
            {
                Console.WriteLine(parser.UsageInfo.GetOptionsAsString(20, 100));
                return 0;
            }
            else if (parser.HasErrors)
            {
                Console.WriteLine(parser.UsageInfo.GetErrorsAsString(78));

                Console.WriteLine("Press any key to continue...");
                Console.ReadKey(true);
                return -1;
            }

            var p = new Program();
            return p.Run(options);
        }

        private int Run(Options options)
        {
            if (options.List)
            {
                return ListAssemblies(options);
            }

            if (options.Export)
            {
                return Export(options);
            }

            return 0;
        }

        private int Export(Options options)
        {
            var sql = @"SELECT  f.assembly_id, f.name, f.file_id, f.content FROM sys.assembly_files AS f WHERE f.assembly_id = COALESCE(@id, f.assembly_id)";
            var result = new List<AssemblyFileModel>();

            using (var connection = new System.Data.SqlClient.SqlConnection(this.GetConnectionString(options)))
            {
                connection.Open();

                var command = connection.CreateCommand();
                command.CommandText = sql;

                if (options.AssemblyId == 0)
                {
                    command.Parameters.AddWithValue("@id", DBNull.Value);
                }
                else
                {
                    command.Parameters.AddWithValue("@id", options.AssemblyId);
                }

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var tmp = new AssemblyFileModel();

                        tmp.AssemblyId = DataExtractor.To<int>(reader, "assembly_id");
                        tmp.Name = DataExtractor.To<string>(reader, "name");
                        tmp.FileId = DataExtractor.To<int>(reader, "file_id");

                        var ordinal = reader.GetOrdinal("content");
                        if (!reader.IsDBNull(ordinal))
                        {
                            tmp.Content = reader.GetSqlBytes(ordinal).Value;
                        }

                        result.Add(tmp);
                    }
                }
            }

            foreach (var item in result)
            {
                var path = Path.GetFileName(item.Name);
                path = path.Replace(".dll", "");
                path = Path.Combine(options.OutPutFolder, path);
                path = path + ".dll";

                File.WriteAllBytes(path, item.Content);
            }

            return 0;
        }

        private string GetConnectionString(Options options)
        {
            System.Data.SqlClient.SqlConnectionStringBuilder connStrBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
            connStrBuilder.DataSource = options.ServerName;
            connStrBuilder.InitialCatalog = options.DatabaseName;
            connStrBuilder.IntegratedSecurity = options.IntegratedSecurity;
            connStrBuilder.ApplicationName = "CLR Exporter";

            if (!options.IntegratedSecurity)
            {
                connStrBuilder.UserID = options.UserName;
                connStrBuilder.Password = options.Password;
            }
            return connStrBuilder.ToString();
        }

        private int ListAssemblies(Options options)
        {
            var sql = @"SELECT A.assembly_id, A.name, A.clr_name, A.permission_set_desc FROM sys.assemblies A WHERE A.is_user_defined = 1";

            var result = new List<AssemblyModel>();

            using (var connection = new System.Data.SqlClient.SqlConnection(GetConnectionString(options)))
            {
                connection.Open();

                var command = connection.CreateCommand();
                command.CommandText = sql;


                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var tmp = new AssemblyModel();
                        tmp.Name = DataExtractor.To<string>(reader, "name");
                        tmp.ClrName = DataExtractor.To<string>(reader, "clr_name");
                        tmp.PermissionSetDesc = DataExtractor.To<string>(reader, "permission_set_desc");
                        tmp.AssemblyId = DataExtractor.To<int>(reader, "assembly_id");

                        result.Add(tmp);
                    }
                }
            }

            var sb = new StringBuilder();
            int maxName = (from n in result select n.Name.Length).Max();
            int maxClrName = (from n in result select n.ClrName.Length).Max();
            int maxPerm = (from n in result select n.PermissionSetDesc.Length).Max();
            int maxId = (from n in result select n.AssemblyId.ToString().Length).Max();

            string format = "{0,-" + maxId + "}\t{1,-" + maxName + "}\t{2,-" + maxClrName + "}\t{3,-" + maxPerm + "}";
            
            sb.AppendFormat(format, "Id", "Name", "CLR Name", "Permission");
            sb.AppendLine();
            sb.AppendFormat(format, "".PadLeft(maxId, '-'), "".PadLeft(maxName, '-'), "".PadLeft(maxClrName, '-'), "".PadLeft(maxPerm, '-'));
            sb.AppendLine();

            foreach (var item in result)
            {
                sb.AppendFormat(format, item.AssemblyId, item.Name, item.ClrName, item.PermissionSetDesc);
                sb.AppendLine();
            }

            Console.WriteLine(sb.ToString());

            return 0;
        }
    }
}
